<?php
/**
 * $Id: db.php 19 2009-06-08 08:45:14Z eyou80 $
 */

/** 载入配置类 **/
require_once 'tinyask/config.php';

/** 载入sql构造类 **/
require_once 'tinyask/db/sql.php';

/**
 * 数据支持类.
 *
 * @package Db
 */
class Tinyask_Db
{
	/** 排序方式 **/
	const SORT_ASC = 'ASC';
	const SORT_DESC = 'DESC';

	/** 表链接方式 **/
	const INNER_JOIN = 'INNER';
	const OUTER_JOIN = 'OUTER';
	const LEFT_JOIN = 'LEFT';
	const RIGHT_JOIN = 'RIGHT';

	/** 数据库操作方式 **/
	const SELECT = 'SELECT';
	const UPDATE = 'UPDATE';
	const INSERT = 'INSERT';
	const DELETE = 'DELETE';

	/**
	 * 实例化的数据库对象
	 *
	 * @var Tinyask_Db
	 */
	private static $_instance;

	/**
	 * 适配器
	 *
	 * @var Tinyask_Db_Adapter
	 */
	private $_adapter;

	/**
	 * 适配器名称
	 *
	 * @var string
	 */
	private $_adapterName;

	/**
	* 表前缀
	*
	* @var string
	*/
	private $_prefix;

	/**
	 * 数据库配置信息
	 *
	 * @var Tinyask_Config
	 */
	private $_config;

	/**
	 * 数据库连接资源标识符
	 *
	 * @var resource
	 */
	private $_link;

	public function __construct($adapterName, array $config, $prefix = 'tinyask_')
	{
		$this->_adapterName = $adapterName;
		$this->_prefix = $prefix;

		require_once 'tinyask/db/adapter/' . strtolower(str_replace('_', '/', $adapterName)) . '.php';
		$adapterName = 'Tinyask_Db_Adapter_' . $adapterName;

		if(!call_user_func(array($adapterName, 'isAvailable'))) {
			require_once 'tinyask/db/exception.php';
			throw new Tinyask_Db_Exception("Adapter $adapterName is not available");
		}

		$this->_config = Tinyask_Config::factory($config);
		$this->_link = null;

		$this->_adapter = new $adapterName();
	}

	/**
	 * 获取适配器名称
	 *
	 * @return string
	 */
	public function getAdapterName()
	{
		return $this->_adapterName;
	}

	/**
	 * 获取表前缀
	 *
	 * @return string
	 */
	public function getPrefix()
	{
		return $this->_prefix;
	}

	/**
	 * 获取sql语句构造器实例化对象
	 *
	 * @return Tinyask_Db_Sql
	 */
	public function sql()
	{
		return new Tinyask_Db_Sql($this->_adapter, $this->_prefix);
	}

	/**
	 * 设置数据库对象
	 *
	 * @param Tinyask_Db $db
	 */
	public static function set(Tinyask_Db $db)
	{
		self::$_instance = $db;
	}

	/**
	 * 获取数据库的实例化对象
	 *
	 * @throws Tinyask_Db_Exception
	 * @return Tinyask_Db
	 */
	public static function get()
	{
		if(empty(self::$_instance)) {
			require_once 'tinyask/db/exception.php';
			throw new Tinyask_Db_Exception('Lost Database Object');
		}
		return self::$_instance;
	}

	/**
	 * 查询记录操作
	 *
	 * @param mixed $field
	 * @return Tinyask_Db_Sql
	 */
	 public function select()
	{
		$args = func_get_args();
		return call_user_func_array(array($this->sql(), 'select'), $args ? $args : array('*'));
	}

	/**
	 * 更新记录操作
	 *
	 * @param string $table
	 * @return Tinyask_Db_Sql
	 */
	public function update($table)
	{
		return $this->sql()->update($table);
	}

	/**
	 * 删除记录操作
	 *
	 * @param string $table
	 * @return Tinyask_Db_Sql
	 */
	public function delete($table)
	{
		return $this->sql()->delete($table);
	}

	/**
	 * 插入记录操作
	 *
	 * @param string $table
	 * @return Tinyask_Db_Sql
	 */
	public function insert($table)
	{
		return $this->sql()->insert($table);
	}

	/**
	 * 执行查询
	 *
	 * @param mixed $sql 查询语句或查询对象
	 * @param string $action 操作动作
	 * @return mixed
	 */
	public function query($sql, $action = self::SELECT)
	{
		/** 在适配器中执行查询 */
		if($sql instanceof Tinyask_Db_Sql) {
			$action = $sql->getAttribute('action');
		} elseif(!is_string($sql)) {/** 如果query不是对象也不是字符串,那么将其判断为查询资源句柄,直接返回 */
			return $sql;
		}

		/** 按需连接 **/
		if(!isset($this->_link)) {
			$this->_link = $this->_adapter->connect($this->_config);
		}

		$handle = $this->_link;

		/** 执行查询 **/
		$resource = $this->_adapter->query($sql, $action, $handle);

		/** 根据查询动作返回相应资源 **/
		if($action) {
			switch($action) {
				case self::UPDATE:
				case self::DELETE:
					return $this->_adapter->affectedRows($resource, $handle);
				case self::INSERT:
					return $this->_adapter->lastInsertId($resource, $handle);
				case self::SELECT:
				default:
					return $resource;
			}
		} else {
			/** 直接执行查询语句则返回资源 **/
			return $resource;
		}
	}

	/**
	 * 取出所有记录
	 *
	 * @param mixed $sql
	 * @param array $filter
	 * @return array
	 */
	public function fetchAll($sql, array $filter = null)
	{
		$resource = $this->query($sql);
		$result = array();

		/** 取出过滤器 **/
		if(!empty($filter)) {
			list($object, $method) = $filter;
		}

		while($row = $this->_adapter->fetch($resource)) {
			$result[] = $filter ? call_user_func(array(&$object, $method), $row) : $row;
		}

		return $result;
	}

	/**
	 * 取出一行
	 *
	 * @param mixed $sql
	 * @param array $filter
	 * @return array
	 */
	public function fetchRow($sql, array $filter = null)
	{
		$resource = $this->query($sql);

		/** 取出过滤器 **/
		if(!empty($filter)) {
			list($object, $method) = $filter;
		}

		$row = $this->_adapter->fetch($resource);

		return $row ? ($filter ? $object->$method($row) : $row) : array();
	}

	/**
	 * 取出一行
	 *
	 * @param mixed $sql
	 * @param array $filter
	 * @return object
	 */
	public function fetchObject($sql, array $filter = null)
	{
		$resource = $this->query($sql);

		/** 取出过滤器 **/
		if(!empty($filter)) {
			list($object, $method) = $filter;
		}

		$row = $this->_adapter->fetchObject($resource);

		return $row ? ($filter ? $object->$method($row) : $row) : new stdClass();
	}
}
